// Copyright 2007 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.


package com.google.enterprise.connector.afyd;

import com.google.gdata.client.appsforyourdomain.UserService;
import com.google.gdata.data.appsforyourdomain.Login;
import com.google.gdata.data.appsforyourdomain.provisioning.UserEntry;
import com.google.gdata.data.appsforyourdomain.provisioning.UserFeed;
import com.google.gdata.data.BaseFeed;
import com.google.gdata.data.DateTime;
import com.google.gdata.util.AuthenticationException;
import com.google.gdata.util.NotModifiedException;

import junit.framework.TestCase;

import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Collections;
import java.net.URL;

/**
 * This class is a test case that verifies several properties of the
 * AfydBackedUserList implementation, namely:
 * (1) the service is queried on the first get()
 * (2) the service is not queried on the second get()
 * (3) the service is queried again after the ttl expires()
 * (4) the results are not changed when a NotModifiedException occurs
 * 
 * @author amsmith@google.com (Adam Smith)
 */
public class AfydBackedUserListTest extends TestCase {

  public void testScript() throws AuthenticationException {
    final int POSITIVE_ONE_DAY = 86400;
    final int NEGATIVE_ONE_DAY = -86400;
    
    // "foo", "bar", and "baz" are users for the first call
    List firstList = new ArrayList(3);
    firstList.add("foo");
    firstList.add("bar");
    firstList.add("baz");
    
    
    List sortedFirstList = new ArrayList(firstList.size());
    sortedFirstList.addAll(firstList);
    Collections.sort(sortedFirstList);
    
    // "baz" is the only user after the third call
    List thirdList = new ArrayList(1);
    thirdList.add("baz");
    
    // assemble these into a script, triggering NotModified on the second call
    List script = new ArrayList(3);
    script.add(firstList);
    script.add(null);
    script.add(thirdList);
    
    UserService service = new ScriptedUserService(script);
    
    AfydBackedUserList abul = new AfydBackedUserList("example.com",
                                                     "admin@example.com",
                                                     "secret",
                                                     service);
    // In the following code, setting the ttl to NEGATIVE_ONE_DAY will (ideally)
    // force the list to refetch on the next access while setting it to
    // POSITIVE_ONE_DAY will keep it from refetching if a fetch has just
    // occurred.
    
    // Also not that assertEquals uses the equals() from AbstractCollection to
    // check equality of individual elements, not hash codes.
    
    // First, make sure it fetches the first list from the script.
    abul.setTtl(NEGATIVE_ONE_DAY);
    abul.size();
    abul.setTtl(POSITIVE_ONE_DAY);
    assertEquals(sortedFirstList, abul);


    // Second, make sure the NotModified exception leaves the list unchanged.
    abul.setTtl(NEGATIVE_ONE_DAY);
    abul.size();
    abul.setTtl(POSITIVE_ONE_DAY);
    assertEquals(sortedFirstList, abul);
    
    
    // Third. make sure the list is checked when the ttl has just expired.
    abul.setTtl(0);
    assertEquals(thirdList, abul);
  }

  /**
   * This class hacks UserService to skip authentication as well as return
   * a scripted response to getFeed().
   */
  public static class ScriptedUserService extends UserService {

    int callCount;    
    List script;
    
    /**
     * Constructs a UserService which returns results from a script (List).
     * @param script a list of lists of usernames to be returned after each call
     * Note: null indicates a NotModifiedException should be thrown 
     */
    public ScriptedUserService (List script) {
      super("Google-AfydConnectorTest-0");
      this.callCount = 0;
      this.script = script;
    }
    
    public void setUserCredentials(String email, String password) 
      throws AuthenticationException {
      if (email.endsWith("@example.com") && password.equals("secret")) {
        return;
      } else {
        throw new AuthenticationException("Failed rigged login.");
      }
    }
    
    public BaseFeed getFeed(URL feedUrl, Class klass, DateTime ifModifiedSince)
    throws NotModifiedException {
      List userList = 
        (script.size() > callCount) ? (List) script.get(callCount++) : null;
      if (userList == null) {
        throw new NotModifiedException();
      } else {
        List entryList = new LinkedList();
        for (Iterator iter = userList.iterator(); iter.hasNext(); ) {
          UserEntry userEntry = new UserEntry();
          Login login = new Login();
          login.setUserName((String) iter.next());
          userEntry.addExtension(login);
          entryList.add(userEntry);
        }
        UserFeed userFeed = new UserFeed();
        userFeed.getEntries().addAll(entryList);
        return userFeed;
      }
    }
  }
}
